package org.NooLab.glue.transaction;

import java.io.Serializable;
import java.util.*;

import org.NooLab.glue.MessageBoard;
 
import org.NooLab.glue.components.MessageBoardFactory;
import org.NooLab.glue.components.MessageBoardFactoryProperties;
import org.NooLab.glue.components.SenderWrap;
import org.NooLab.glue.interfaces.StateChangeIntf;
import org.NooLab.glue.msgframe.Notification;
import org.NooLab.glue.msgframe.XRegistration;
import org.NooLab.glue.storage.MsgBoardDataStorage;
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.strings.ArrUtilities;
import org.NooLab.utilities.strings.StringsUtil;
import org.NooLab.utilities.timing.DelayFor;





/**
 * 
 * TODO  	do not load transact state == 1 == opened, they never have been confirmed
 * 			instead put an error flag while loading / saving
 * 
 * 
 * 
 */
public class Transactions implements Serializable{

	transient private static final long serialVersionUID = -7298188807209017188L;
	// =================================

	// constants ......................


	// object references ..............
	transient MessageBoardFactory mbf;
	//transient MessageBoard messageBoard ;
	transient MsgBoardDataStorage storage;
	
	transient MessageBoardFactoryProperties factoryProperties;
	transient MessageBoard  mb ;
	
	StateChangeIntf stateChangeInfo; 
	
	// main variables / properties ....
	
	Vector<Transaction> transactions = new Vector<Transaction>();
	String filename="";
	String role="" ;
	
	boolean informClientsAboutState=true;
	
	// volatile variables .............
	transient CleaningTransactions cleaningTransactions ;
	transient long serialUID=0;
	
	Vector<AnnotatedDelivery> annotatedDeliveries = new Vector<AnnotatedDelivery>(); 
	
	// helper objects .................
	
	transient ArrUtilities arrutil = new ArrUtilities ();
	transient DFutils fileutil = new DFutils();
	transient PrintLog out = new PrintLog (2,true) ; 
	transient StringsUtil strgutil = new StringsUtil() ;
	
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public Transactions( MessageBoardFactory mbf){
		this.mbf = mbf ;
		long[] openT;
		long xid;
		
		// messageBoard = mbf.getMessageBoard() ;
		storage =  mbf.getMessageBoardStorage() ;
			// messageBoard.getMsgBoardStorage() ;
		role = mbf.getFactoryProperties().getParticipantsRole().toLowerCase().trim() ;
		if (role.length()>=3){
			role = "-"+strgutil.replaceAll(role, new String[]{"a","e","o","u","i"}, "", false).substring(0, 3);
		}
		
		// if it is unique, with preferred ID, we already know about it?
		// xid = mbf.getFactoryProperties().getParticipantFactory().getInstanceID();
		if (mbf.getMessageBoard() == null){
			xid = mbf.getFactoryProperties().getParticipantFactory().config().getPreferredID();
			if (xid>0){
				serialUID = xid;
			}
		}
		
		// 
		createFilename( "msb"+role,serialUID) ; 
		load();
		
		openT = getOpenTransactions( new int[]{Transaction.TS_TASK_CONFIRMED,Transaction.TS_OPENED}) ; 
		
		factoryProperties = mbf.getFactoryProperties();
		if (factoryProperties.getParticipantsRole().toLowerCase().contentEquals("board")){
			
			cleaningTransactions = new CleaningTransactions( this, mbf, out);
			cleaningTransactions.go();
		}
	}
	
	public Transactions( ){
	}
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

	

	/**
	 * for persistence of MessageBoard, the Notifications have to be stored
	 */
	synchronized public void save(){
		
		int i,j;
		long tid;
		Transaction t;
		
		try{
			
			// from last to first index: remove doubles
			
			i=0;
			while (i<transactions.size() ){
				t = transactions.get(i);
				tid = t.getTransactionID() ;
				
				j=i+1;
				while (j<transactions.size() ){
					if (tid == transactions.get(j).getTransactionID() ){
						transactions.remove(j);
						j--;
					}
					j++;
				}
				i++;
			} // i->
			
			
			
			// e.g.: D:/dev/java/data/test/mb/hibernation/~transactions-msb-0.lst
			storage.storeTransactionDataObj( transactions, filename) ;
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	

	public void load(){
		load(1) ;
	}
	
	/**
	 * selectionMode ...
	 *  = 0  -> all  
	 *  = 1  -> only opened == those not closed
	 *  = 2  -> only closed or canceled, but not archived
	 *  = 3  -> only archived
	 *  
	 *  
	 *  
	 * @param selectionMode
	 */
	@SuppressWarnings({ "unused", "unchecked" })
	public void load( int selectionMode ){
		Object obj;
		boolean hb;
		int state,n;
		Transaction t;
		long tiid, xid;
		
		// D:/dev/java/data/test/mb/hibernation/msb-src/~transactions-msb-src-0.lst
		try{
			if (fileutil.fileexists(filename)==false){
				
				return;
			}
			
												out.print(3, "loading transactions dictionary (mode:"+selectionMode+") from file : "+filename) ;
			obj = storage.readTransactionDataObj(filename) ;
			transactions  = (Vector<Transaction>)obj ;
												out.print(4, "detected : "+transactions.size());
												
			// xid = mbf.getParticipantReference().getxRegistration().getInstanceID() ;	
			// we have to clean it later and explicitly, since the ParticipantReference is not known at this time...
	        // this time: construction of mbf
												
			int i=transactions.size()-1;
			while (i>=0){
				
				hb = false;
				t = transactions.get(i) ;
				state = t.getTransactState() ;

				tiid = t.getInstanceID() ;
				if (serialUID!=0){

					if (tiid!=serialUID){
						transactions.remove(i) ;
						continue;
					}
				}
				
				if (selectionMode==0){
					if (state>=0){
						if (state != Transaction.TS_TASK_CANCELED){
							hb = true;
						}
					}
				}
				if (selectionMode<0){
					if ((state<0) || (t.getTransactErrorState()<0)){
						hb = true;
					}
				}
				if ((selectionMode==1) && (state>=0) && (t.getTransactErrorState()==0)){
					if ((state<Transaction.TS_RESULTS_RETURNED) && 
						(state!=Transaction.TS_TASK_CANCELED) 
						// && (state!=Transaction.TS_TASK_DENIED)
					){
						hb=true;
					}
				}
				if ((selectionMode==2) && (state>=0)){
					if ((state<Transaction.TS_CLOSED) && 
						(state==Transaction.TS_TASK_CANCELED) &&
						(state!=Transaction.TS_TASK_DENIED)){
						
						hb=true;
					}
				}
				if (selectionMode==3){
					
				}
				
				if (hb==false){
					transactions.remove(i) ;
				} else {
					// avoiding zombies...
					if ( (state == Transaction.TS_TASK_DELIVERING) || 
							(state == Transaction.TS_TASK_DELAYED) || 
							(state == Transaction.TS_TASK_STARTDELIVERY) ) {
						t.setTransactState( Transaction.TS_TASK_ACCEPTED ); 
					}
					t.setTransactErrorState(0) ;
				}
				i--;
			} // i->
												out.print(4, "loaded : "+transactions.size());
			for (i=0;i<transactions.size();i++){
				t = transactions.get(i) ;
				state = t.getTransactState();
				if ((state>0) && (state<Transaction.TS_TASK_CANCELED)){
					t.setTransactState( Transaction.TS_TASK_ACCEPTED ); 
				}
				t.setTransactErrorState(0) ;
			}
			 
		}catch(Exception e){
			
		}
	}
	
	
	protected String createFilename( String name, long serialUID){
		String path, filname = "";
		
		
		path = storage.getHibernationPath( name ) ;
		 
		filname = "~transactions-"+name+"-"+serialUID+".lst" ;
		filname = fileutil.createPath( path, filname) ;
		 
		filename = filname;
		
		return filname;
	}
	

	/** 
	 * this considers a subset of open transactions, not in error state, at least TS_OPENED,
	 * not yet S_RESULTS_RETURNED, but not TS_TASK_TIMEDOUT or TS_TASK_CANCELED<br/>
	 * additional excluding state values can be provided as int[]
	 * 
	 * @return
	 */
	public int countActiveTransactions(){
		int[] sv = new int[0];

		return countOpenTransactions(sv);
	}

	public int countActiveTransactions(  int[] addExclStateValues ){
		
		int n,i,result = 0,sv,esv,ix , transactState;
		boolean hb;
		Transaction t;
		long tid;
		 
		
		// statevalues = (Vector<Integer>)(new Vector(Arrays.asList(specStateValues))) ;
		// this puts the list as an element of the vector ...
		// we can check the array directly..
		
		Arrays.sort(addExclStateValues) ; // necessary for binarySearch!
		
		i=0;
		n=transactions.size();
		while (i<n){
			
			t = transactions.get(i) ;
			
			if (t!=null){
				tid = t.getTransactionID() ;
				transactState = t.getTransactState();
				 
				esv = t.getTransactErrorState() ;
				hb = false;
				
				if ((esv == Transaction.TS_ERR_NO) && 
					(transactState >= Transaction.TS_OPENED) &&
					(transactState <  Transaction.TS_RESULTS_RETURNED) &&
					(transactState != Transaction.TS_TASK_CANCELED) &&
					(transactState != Transaction.TS_TASK_TIMEDOUT) 
				){
					hb = true;
					ix = Arrays.binarySearch(addExclStateValues, transactState);
					if (ix>=0) {
						hb=false;
					}
				} // transactions: no error AND transaction not closed
				if (hb){
					result++;
				}
			} // transaction != null ?
			i++;
		}
		
		return result ;
	}

	public int countOpenTransactions(){
		int[] sv = new int[0];
		return countOpenTransactions(sv);
		
	}
	
	
	
	public int countOpenTransactions( int[] specStateValues ){
		int n,i,result = 0,sv,esv,ix , transactState;
		Transaction t;
		long tid;
		 
		
		// statevalues = (Vector<Integer>)(new Vector(Arrays.asList(specStateValues))) ;
		// this puts the list as an element of the vector ...
		// we can check the array directly..
		
		Arrays.sort(specStateValues) ; // necessary for binarySearch!
		
		i=0;
		n=transactions.size();
		while (i<n){
			
			t = transactions.get(i) ;
			
			if (t!=null){
				tid = t.getTransactionID() ;
				transactState = t.getTransactState();
				sv = t.getTransactState() ;
				esv = t.getTransactErrorState() ;
				
				if ((esv == Transaction.TS_ERR_NO) && (transactState< Transaction.TS_CLOSED)){
					
					ix = Arrays.binarySearch(specStateValues, sv);
					if (ix>=0) {
						result++;
					}
				} // transactions: no error AND transaction not closed
				
			} // transaction != null ?
			i++;
		}
		
		return result ;
	}
	

	public long[] getOpenTransactions( int[] specStateValues ){
		int n,i,sv,esv ,ix, transactState;
		long tid;
		Transaction t;
		Vector<Integer> statevalues ;
		Vector<Long> openT = new Vector<Long>() ;
		long[] openTransactions = new long[0] ;
		
		if (specStateValues.length==0){
			specStateValues = new int[]{Transaction.TS_OPENED};
		}
		 
		Arrays.sort(specStateValues) ; // necessary for binarySearch!
		
		i=0;
		n=transactions.size();
		while (i<n){

			t = transactions.get(i) ;
			
			if (t!=null){
				tid = t.getTransactionID() ;
				transactState = t.getTransactState();
				sv = t.getTransactState() ;
				esv = t.getTransactErrorState() ;
				
				if ((esv == Transaction.TS_ERR_NO) && (transactState< Transaction.TS_CLOSED)){
					
					ix = Arrays.binarySearch(specStateValues, sv);
					if (ix>=0) {
						openT.add( t.getTransactionID() ) ;
					}
				} // transactions: no error AND transaction not closed
				
			} // transaction != null ?
			i++;
		}
		
		openTransactions = arrutil.changeArrayStyle(openT, 0L);
		
		return openTransactions ;
	}
	
	public int cleanFromInconsistentItems(){
		Transaction t;
		boolean hb ;
		long xid,tiid;
		
		xid = mbf.getParticipantReference().getxRegistration().getInstanceID() ;	
		
		int i=0;
		while (i<transactions.size()){
			
			t = transactions.get(i);
			
			tiid = t.instanceID ;
			if (xid != tiid){
				transactions.remove(i); // not for this client...
				// this situation may happen, if there was a failed attempt of this source to register as a unique instance
				i--;
			} else{
				hb = true;
			
				if (hb){ hb = (t.clientSecret!=null) && (t.clientSecret.length()>0); }
				
				if (hb){ hb = (t.contentType!=null)  && (t.contentType.length()>0); }
				if (hb){ hb = (t.dataObject != null) && (t.dataObject.length()<2); }
			
				if (hb==false){
					t.setTransactState(Transaction.TS_ERR_TRANSINVALID);
				}
			}
			i++;
		} // i->
		
		return transactions.size();
	}
	
	
	public void annotateTransactionTaskForDelivery( int index, String guidStr , long instanceID ){
		AnnotatedDelivery annotatedDelivery; 
		
		annotatedDelivery = new AnnotatedDelivery(index, guidStr, instanceID );
		
		annotatedDeliveries.add( annotatedDelivery );
	}
	
	// these two methods set flags such that callback routines can be informed about the change
	public void stateChangedFor( long transactionID ){
		int value = -1;
		String description="";
		
		// look for it
		if (stateChangeInfo!=null){  // TODO: should not be null, where is it initialized ???
			stateChangeInfo.capturingStateValueChanges( transactionID, value, description );
		}
	}
	
	public void stateChanged(){
		
		// get all of the changes 
		
	}
	
	// ------------------------------------------------------------------------
	public void setMessageBoardFactory(MessageBoardFactory mbf){
		this.mbf = mbf ;
	}
	
	public void archiveClosedTransactions(){
		
	}
	
	public void bundlingArchivedTransactions(){
		
	}
	
	public int size(){
		
		return transactions.size() ;
	}
	
	
	
	public long getLastTransactionID(){
		int tstate , terrstate ;
		long latestTID=-1;
		Transaction transaction;
		
		if (transactions.size()>0){
			transaction = transactions.get(transactions.size()-1);
			tstate = transaction.getTransactState() ;
			terrstate = transaction.getTransactErrorState() ;
			if (terrstate==0){ 
				latestTID = transaction.getTransactionID() ;
			}
		}
		return latestTID;
	}
	
	/**
	 * 
	 * mode=1 -> all that are NOTclosed
	 * mode=0 -> all that are closed
	 * 
	 * 
	 * @param idval
	 * @param mode
	 * @return
	 */
	public int getIndexByTransactionID( long idval , int mode){
		int index = -1;
		Transaction t;
		boolean hb;
		for (int i=0;i<transactions.size();i++){
			
			t = transactions.get(i) ;
			if (t.getTransactionID() == idval){
				hb = mode==0; // all
				if (hb==false){
					if (mode==1){
						hb = (t.transactState>0) && (t.transactState<t.TS_CLOSED);
					}
					if (mode<0){
						hb = (t.transactState<0) || (t.transactState>=t.TS_CLOSED);
					}
				}
				index = i;
				break;
			}
		} // i->
		
		return index ;
	}
	
	public int getIndexByStates( int[] stateValues, int mode) {
		ArrayList<Long> exludedIDs = new ArrayList<Long> ();
		int beyondIndex = -1 ; 
		
		return getIndexByStates(stateValues,exludedIDs,beyondIndex,mode );
	}
	
	public int getIndexByStates( int[] stateValues, ArrayList<Long> exludedIDs , int beyondIndex, int mode) {
		
		int index = -1, sv,ec, start, stateVal;
		Transaction t;
		boolean hb;
		start = beyondIndex+1;
		String str ;
		
		if ((start>=transactions.size()) || (stateValues==null) || (stateValues.length==0)){
			return -1;
		}
										out.print(4,"Transactions.getIndexByStates(): number of transactions to sift through : n="+transactions.size());
		for (int i=start;i<transactions.size();i++){
			
			t = transactions.get(i) ;
			hb = false;
			
			sv = t.getTransactState() ;  str = Transaction.decodeTransactionCode(sv) ;
			ec = t.getTransactErrorState() ;
										out.print(5, "i="+i+"  checked transaction (tid="+t.transactionID+") has state : "+sv+" = "+ str);
			if (ec<0){
				continue;
			}
			
			for (int s=0;s<stateValues.length;s++){ 
				hb = false;
				
				stateVal = stateValues[s];
				
				if (mode<0){
					hb = sv <= stateVal;
				}
				if (mode==0){
					hb = sv >= stateVal;
				}
				if (mode>=1){
					hb = sv == stateVal;
				}
				
				if (hb){
					hb = true;
					break;
				}
			}
			
			
			if (hb){
				if ((exludedIDs!=null) && (exludedIDs.indexOf(i)<0)){
					index = i;
					break;
				}
			}
			
		}
		
		return index; 
		
		
	}
	
	/**
	 * 
	 * @param stateVal
	 * @param exludedIDs
	 * @param beyondIndex
	 * @param mode
	 * @return
	 */
	public int getIndexByState( int stateVal, Vector<Long> exludedIDs , int beyondIndex, int mode){
		
		
		int index = -1, sv, start;
		Transaction t;
		boolean hb;
		start = beyondIndex+1;
		
		if (start>=transactions.size()){
			return -1;
		}
		
		for (int i=start;i<transactions.size();i++){
			
			hb=false;
			t = transactions.get(i) ;
			
			sv = t.getTransactState() ;
			
			if (mode<0){
				hb = sv <= stateVal;
			}
			if (mode==0){
				hb = sv >= stateVal;
			}
			if (mode>=1){
				hb = sv == stateVal;
			}
			
			if (hb){
				if ((exludedIDs!=null) && (exludedIDs.indexOf(i)<0)){
					index = i;
					break;
				}
			}
			
		}
		
		return index; 
		
	}
	
	/**
	 * 
	 * @param stateVal
	 * @param exludedIDs
	 * @param mode
	 * @return
	 */
	public int getIndexByState( int stateVal, Vector<Long> exludedIDs , int mode){
		return getIndexByState( stateVal,exludedIDs,-1, mode);
	}
	
	public int getIndexByState( int stateVal, int beyondIndex, int mode){
		Vector<Long> vv = new Vector<Long>();
		return getIndexByState( stateVal,vv,beyondIndex, mode);
	}
	/**
	 * 
	 * 
	 * @param stateVal
	 * @param mode -1= <than provided state value, 0=at least , 1=exactly
	 * @return
	 */
	public int getIndexByState( int stateVal, int mode){
		
		Vector<Long> vv = new Vector<Long>();
		
		return getIndexByState( stateVal,vv, mode);
	}
	
	
	
	public Transaction getTransactionByID( long idval ){
		 
		Transaction rt=null, t=null;
		
		for (int i=0;i<transactions.size();i++){
			
			t = transactions.get(i) ;
			if (t.getTransactionID() == idval){
				rt = t; 
				break;
			}
		} // i->
		
		return rt ;
	}

	
	
	
	public int[] getTransactionIndexesByInstanceID( long idval ){
		
		int[] transIDs = new int[0] ;
		Vector<Integer> tids = new Vector<Integer>(); 
		Transaction t ;
		
		for (int i=0;i<transactions.size();i++){
			
			t = transactions.get(i) ;
			if (t.getInstanceID() == idval){				 
				tids.add( i) ;
				break;
			}
		} // i->
		
		if (tids.size()>0){
			arrutil.changeArrayStyle(tids, 0) ;
		}
		
		return transIDs;
	}
	
	public long[] getTransactionIDsByStateValues( int[] stateValues, int mode){
		
		int sv, six;
		boolean hb;
		long tid;
		long[] tids = new long[0];
		Vector<Long> tidc = new Vector<Long> ();
		
		
		
		try{
			
			for (int i=0;i<transactions.size();i++){
				
				tid = transactions.get(i).transactionID ;
				sv = transactions.get(i).getTransactState() ;
				hb = false;
				
				for (int s=0;s<stateValues.length;s++){
				
					if (mode<0){
						hb = (sv<=stateValues[s]);
					}
					if (mode==0){
						hb = (sv>=stateValues[s]);
					}
					if (mode>=1){
						hb = (sv==stateValues[s]);
					}
					
					if (hb){
						six = s;
						break;
					}
				} // s->
				
				if (hb){
					tidc.add(tid) ;
				}
				
			} // ->
			
			if (tidc.size()>0){
				tids = arrutil.changeArrayStyle(tidc, 0L);
			}
			
		}catch(Exception e){
			
		}
		
		
		return tids; 
	}
	
	public long[] getTransactionIDsByInstanceID( long idval ){
		
		long[] tids; 
		int[] ixes; 
		
		ixes = getTransactionIndexesByInstanceID( idval ) ;
		
		tids = new long[ixes.length];
		
		for (int i=0;i<ixes.length;i++){
			tids[i] = transactions.get(i).getTransactionID() ;
		}
		
		return tids;
	}
	
	
	public void addItem( Transaction t ){
		transactions.add(t) ;
		save();
	}
	
	/**
	 * 
	 * depending on settings, delete or archive: XXX NOT READY !!
	 * 
	 * @param t
	 */
	public void removeItem( Transaction t ){
		int p;
		boolean hb=false;
		p = t.index ;
		
		if ((p>=0) && (p<transactions.size())){
			if ( (transactions.get(p).instanceID == t.getInstanceID()) && 
				 (transactions.get(p).getTransactionID() == t.getTransactionID())) {
				removeItem(p);
				hb=true;
			}
			if (hb){
				save();
			}
		}
	}
	
	public void removeItem( int index ){
		if (( index>=0) && ( index<transactions.size())){
			transactions.remove(index) ;
			save();
		}
	}
	
	public void removeItemByID( long transactionID ){
		int ix;
		
		ix = getIndexByTransactionID(transactionID,1) ;
		
		if (ix>=0){
			removeItem(ix) ;
		}
	}
	
	public void insertItem( int index, Transaction t ){
		boolean hb=false;
		if (index<0){
			index=0;
		}
		if (index>transactions.size()){
			transactions.add(t) ;
			hb=true;
		}else{
			if (( index>=0) && ( index<transactions.size())){
				transactions.insertElementAt(t, index) ;
				hb = true;
			}
		}
		if (hb){
			save();
		}
	}

	public Transaction getItem( int index ){
		Transaction t=null;
		
		if ((index>=0) && (transactions!=null) && (index<transactions.size())){
			t = transactions.get(index) ;
		}
		return t ;
	}
	
	
	public int setItem( int index, Transaction t ){
		int result = -1;
		
		if (( index>=0) && ( index<transactions.size())){
			result=0;
			transactions.set(index, t);
		}
		
		return result ;
	}


	public Vector<Transaction> getTransactions() {
		return transactions;
	}


	public void setTransactions(Vector<Transaction> transactions) {
		this.transactions = transactions;
	}
	
	// ========================================================================
	
	public StateChangeIntf getStateChangeInfo() {
		return stateChangeInfo;
	}

	public void setStateChangeInfo(StateChangeIntf stateChangeInfo) {
		this.stateChangeInfo = stateChangeInfo;
	}

	public String getFilename() {
		return filename;
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

	public void stopCleaningDemonProcess(){
		cleaningTransactions.stop();
	}
	public Vector<AnnotatedDelivery> getAnnotatedDeliveries() {
		return annotatedDeliveries;
	}

	public void setAnnotatedDeliveries(Vector<AnnotatedDelivery> annotatedDeliveries) {
		this.annotatedDeliveries = annotatedDeliveries;
	}

	class AnnotatedDelivery{
		int index;
		String guid;
		long instanceID;
		long timestamp;
		
		
		public AnnotatedDelivery( int ix, String guidstr, long iid){
			index = ix;
			guid = guidstr;
			instanceID = iid;
			timestamp = System.currentTimeMillis() ;
		}


		public int getIndex() {
			return index;
		}


		public String getGuid() {
			return guid;
		}


		public long getInstanceID() {
			return instanceID;
		}


		public long getTimestamp() {
			return timestamp;
		}
		
	}
}



class CleaningTransactions implements Runnable{

	Transactions tParent;
	
	MessageBoardFactory mbf;
	MessageBoard mb;
	String xmlstr;
 
	XRegistration xreg ;
	
	String homePath ;
	
	int _old_value = MessageBoardFactoryProperties.__OLD;
	int _veryold_value = MessageBoardFactoryProperties.__VERY_OLD;
	
	PrintLog out;
	DelayFor _delay;
	
	public boolean isRunning=false;
	Thread pcThrd;
	
	
	public CleaningTransactions( Transactions ts, MessageBoardFactory mbf,  PrintLog outprn) {
		 
		tParent = ts;
		
		this.mbf = mbf; 
		mb = mbf.getMessageBoard();
		
		homePath = mbf.getMessageBoardStorage().getHomePath() ;
		
		out = outprn;
		_delay = new DelayFor() ;
		
		pcThrd = new Thread(this,"pconfirmThread");
	}
		
	public void stop() {
		_delay.setUserbreak(true);
		isRunning = false;
		out.delay(300);
	}

	public boolean perform( int z){
		int r,port;
		boolean rB=false, is_old,is_very_old;
		long oldAgeThreshold, iid,tid;
		String path,str, secret ;
		Vector<XRegistration>  xregs ;
		XRegistration  xreg;
		Transaction transaction;
		
		xregs = mb.getMsgBoardShares().getMetaRegistration().getRegisteredInstances();
		
		try{
			
			oldAgeThreshold = (System.currentTimeMillis() - (1000*60*60*24*_veryold_value) ); 
			
			// loaded transactions...
			for (int i=0; i<tParent.transactions.size();i++){
				transaction = tParent.transactions.get(i);
				
				is_very_old = transaction.isOld( _veryold_value,"day")  ; 
				
				if (is_very_old){
					transaction.setTransactState( Transaction.TS_TASK_TIMEDOUT) ;
					
					
					if (tParent.informClientsAboutState){
						iid = transaction.instanceID;
						tid = transaction.transactionID ;
						secret = transaction.clientSecret ;
						
						xreg = XRegistration.getItemByID(iid, xregs) ;
					
						if ( tParent.factoryProperties.getProtocolId() >= MessageBoardFactoryProperties.protocolTCP){
							port = xreg.getClientListeningPort();
							SenderWrap sender = new SenderWrap(mb);
							r = sender.sendingMessage(xmlstr, port);
								if (r<0){
								
								}
							sender=null;
						} // tcp+ ?
						else{
							// for http rest we could issue notifications
						}
					} // inform participant about state change ?
				}
			} // i->
			
			
			// files ... only if allowed by factoryProperties...
			if ( mbf.getFactoryProperties().getAllowFileCleaning()) {
				if ((z==0) || (z % 10 ==0)){
					
					homePath = mbf.getMessageBoardStorage().getHomePath() ; // e.g.  D:/dev/java/data/test/mb/
					
					// D:\dev\java\data\test\mb\transactData
					// D:\dev\java\data\test\mb\transferBuffer\board
					if (isRunning){
						path =  mbf.getMessageBoardStorage().getDataStoragePath() ;
						tParent.fileutil.deleteOldFiles( path, new String[]{"obj"}, oldAgeThreshold ) ;
					}
					
					if (isRunning){
						path =  tParent.fileutil.createPath( homePath, "transferBuffer/board/");
						tParent.fileutil.deleteOldFiles( path, new String[]{"btmp"},oldAgeThreshold ) ;
					}
				}
			} // removing old files allowed ?
			
			tParent.save() ;
			
		}catch(Exception e){
			
		}
		 
		
		return rB;
	}
	
	public void go(){
		
		isRunning = true;
		
		pcThrd.start();
		
	}
	
	
	public void run() {
		String str="";
		int z=0;
		int dd = 100,stepping;
		boolean hb=false , isWorking=false;
		 
		out.delay(5000);
		
		dd = 1000*60*3;   // 3 minutes
		stepping = 60*3*10 ; // 100 ms interrupt delay
			
		try{
			
			while ((isRunning) ){
				
				if (isWorking==false){
					isWorking=true;
												out.printErr(4,"trying to confirm client connect ("+z+")...");
					hb = perform(z);
					
					isWorking=false;
				} // already working ?
				_delay.period( dd, stepping);
				
				if(z>1000){z=0;}
				z++;
			}
			
		}catch(Exception e){
			hb=false;
		}
		
		if (hb){
			str = "trying to confirm the connection/wiring to the participant (id:"+xreg.getInstanceID()+") has been successful." ;
			out.print(3,str);
		}else{
			str = "trying to confirm the connection/wiring to the participant (id:"+xreg.getInstanceID()+") FAILED." ;
			out.printErr(2,str);
		}
											
		xreg.setInstanceConnectionConfirmed(hb);
		
		isRunning = false;
	}
	
}
